﻿@namespace Masa.Blazor.Components.TemplateTable.FilterDialogs
@inject I18n i18N;

<PModal @bind-Value="Show"
        Width="@Width"
        BodyStyle="min-height: 180px"
        Title="@i18N.T("Filters")"
        Persistent
        OnCancel="@HandleOnCancel"
        OnSave="HandleOnSave">

    @for (int i = 0; i < _filters.Count; i++)
    {
        var index = i;
        var filter = _filters[index];
        if (filter.Persistent) continue;
        <div class="d-flex align-center mb-2">
            <div style="width: 86px; flex: none;"
                 class="mr-2 text-right">
                @if (index == 0)
                {
                    <span>@i18N.T("When")</span>
                }
                @* else if (index == 1)
                {
                    <MSelect @bind-Value="@_operator"
                             Items="@Operators"
                             ItemText="u => u"
                             ItemValue="u => u"
                             Class="filter-operator"
                             Dense
                             Outlined
                             HideDetails="@true">
                    </MSelect>
                } *@
                else
                {
                    <span>@i18N.T(@_operator)</span>
                }
            </div>

            <MSelect @bind-Value="@filter.ColumnId"
                     Placeholder="@i18N.T("ColumnId")"
                     Items="_computedColumns"
                     ItemText="u => u.Name"
                     ItemValue="u => u.Id"
                     Label="@i18N.T("ColumnId")"
                     TItem="ColumnInfo"
                     TItemValue="string"
                     TValue="string"
                     Class="mr-2"
                     Style="max-width: 150px"
                     Dense
                     Filled
                     HideDetails="@true"
                     Readonly="filter.Persistent"
                     OnSelect="@filter.OnSelect">
            </MSelect>
            <MSelect TValue="StandardFilter" TItemValue="StandardFilter" TItem="StandardFilter"
                     Value="@filter.Func" Placeholder="@i18N.T("Func")" ValueChanged="(value) => OnFuncChange(index, value)"
                     Items="@filter.FuncList"
                     ItemText="@(u => i18N.T($"SDF_{GetFuncI18n(u, filter.Column.Type)}"))"
                     ItemValue="u => u"
                     Label="@i18N.T("Func")"
                     Class="mr-2"
                     Style="max-width: 150px"
                     Dense
                     Filled
                     Readonly="filter.Persistent"
                     HideDetails="@true">
            </MSelect>
            @if ((filter.Func is StandardFilter.Equals or StandardFilter.NotEquals or StandardFilter.Contains or StandardFilter.NotContains) && filter.SelectOptions is not null)
            {
                bool isList = filter.Func is StandardFilter.Contains or StandardFilter.NotContains;
                if (isList)
                {
                    List<string> values = [.. filter.Expected.Replace('[', ' ').Replace(']', ' ').Replace('"', ' ').Split(',').Select(s => s.Trim())];
                    <MAutocomplete Value="values"
                                   ValueChanged="(List<string> values) => ValueChange(index, values)"
                                   Clearable
                                   Multiple
                                   Items="@filter.SelectOptions"
                                   ItemText="u => u.Label"
                                   ItemValue="u => u.Value"
                                   Label="@i18N.T("Expected")"
                                   Placeholder="@i18N.T("Expected")"
                                   Color="primary"
                                   Class="mr-2 m-input--dense-48"
                                   Style="max-width: 150px"
                                   Dense
                                   Filled
                                   Readonly="filter.Persistent"
                                   HideDetails="@("auto")">
                    </MAutocomplete>
                }
                else
                {
                    <MAutocomplete @bind-Value="@filter.Expected"
                                   Placeholder="@i18N.T("Expected")"
                                   Items="@filter.SelectOptions"
                                   ItemText="u => u.Label"
                                   ItemValue="u => u.Value"
                                   Label="@i18N.T("Expected")"
                                   Class="mr-2 m-input--dense-48"
                                   Style="max-width: 150px"
                                   Dense
                                   Filled
                                   Readonly="filter.Persistent"
                                   HideDetails="@("auto")">
                    </MAutocomplete>
                }
            }
            else if (filter.Type == ExpectedType.DateTime && (filter.Func is not StandardFilter.Set && filter.Func is not StandardFilter.NotSet))
            {
                DateTime? date = DateTime.TryParse(filter.Expected, out var dt) ? dt : null;
                <PDateDigitalClockPicker Value="@date"
                                         ValueChanged="@(v => filter.Expected = (v.ToString() ?? string.Empty))"
                                         TValue="DateTime?"
                                         ViewType="DateTimePickerViewType.Desktop"
                                         TimeFormat="TimeFormat.Hr24"
                                         MultiSection
                                         UseSeconds>
                    <PDefaultDateTimePickerActivator Label="Expected"
                                                     Filled
                                                     Format="yyyy-MM-dd HH:mm:ss" />
                </PDateDigitalClockPicker>
            }
            else if (!_noExpectedFuncs.Contains(filter.Func))
            {
                <MTextField @bind-Value="@filter.Expected"
                            Placeholder="@i18N.T("Expected")"
                            Readonly="filter.Persistent"
                            Label="@i18N.T("Expected")"
                            Class="mr-2 m-input--dense-48"
                            Style="max-width: 150px"
                            Filled
                            Dense
                            HideDetails="@("auto")">
                </MTextField>
            }

            <MSpacer />

            @if (filter.Persistent)
            {
                <div style="min-width: 36px;"></div>
            }
            else
            {
                <MButton IconName="mdi-close"
                         OnClick="@(() => RemoveFilter(filter))">
                </MButton>
            }
        </div>
    }

    <div class="text-right" style="margin-right: 44px;">
        <MButton Text
                 LeftIconName="mdi-plus"
                 Color="primary"
                 OnClick="@AddNewFilter">@i18N.T("Add")</MButton>
    </div>
</PModal>

@code {

    [Parameter] public IList<ColumnInfo> Columns { get; set; } = [];

    /// <summary>
    /// Use this to filter out hidden columns when selecting columns.
    /// </summary>
    [Parameter] public HashSet<string> HiddenColumnIds { get; set; } = [];

    [Parameter] public View? ActiveView { get; set; }

    [Parameter] public EventCallback<Filter> OnSave { get; set; }

    [Parameter] public Filter Filter { get; set; } = new();

    [Parameter] public EventCallback<Filter> FilterChanged { get; set; }

    [Parameter] public StringNumber Width { get; set; } = 720;

    [Parameter] public bool Show { get; set; }

    [Parameter] public EventCallback<bool> ShowChanged { get; set; }

    private static readonly IList<string> Operators = ["and", "or"];

    private static readonly HashSet<StandardFilter> _noExpectedFuncs =
    [
        StandardFilter.Set,
        StandardFilter.NotSet,
        StandardFilter.True,
        StandardFilter.False
    ];

    private HashSet<string> _prevHiddenColumnIds = [];
    private Guid _prevActiveViewId;

    private IList<ColumnInfo> _computedColumns = [];

    /// <summary>
    /// accepted values: "and", "or"
    /// </summary>
    private string _operator = "and";

    protected override void OnParametersSet()
    {
        base.OnParametersSet();

        if (Show && ActiveView is not null)//&& (_prevActiveViewId != ActiveView.Id || !_prevHiddenColumnIds.SetEquals(HiddenColumnIds)))
        {
            _prevActiveViewId = ActiveView.Id;
            _prevHiddenColumnIds = HiddenColumnIds;
            _computedColumns = Columns.Where(u => !HiddenColumnIds.Contains(u.Id)).Where(u => u.Type != ColumnType.Actions).ToList();

            _filters.Clear();
            foreach (var option in Filter.Options)
            {
                var columnName = option.ColumnId;
                var column = Columns.FirstOrDefault(u => u.Id == option.ColumnId || option.Type == ExpectedType.DateTime && u.Id.StartsWith(option.ColumnId));

                if (column is null)
                {
                    continue;
                }

                var filterOption = new FilterModel(column)
                {
                    Func = option.Func,
                    Expected = option.Expected,
                    Persistent = option.Persistent,
                    Type = option.Type,
                };

                _filters.Add(filterOption);
            }

            _operator = Filter.Operator == FilterOperator.And ? "and" : "or";
        }
    }

    private readonly List<FilterModel> _filters = [];

    internal void Open()
    {
        Show = true;
        StateHasChanged();
    }

    private void AddNewFilter()
    {
        if (Columns.Count == 0)
        {
            return;
        }

        var filter = new FilterModel(Columns[0]);

        _filters.Add(filter);
    }

    private void RemoveFilter(FilterModel filterModel)
    {
        if (filterModel.Persistent)
        {
            return;
        }

        _filters.Remove(filterModel);
    }

    private void OnMultiSelect(FilterModel filter)
    {
        filter.Expected = "{ in: [" + string.Join(",", filter.MultiSelect.Select(u => $"\"{u}\"")) + "]}";
    }

    private async Task HandleOnSave()
    {
        await HandleOnCancel();

        Filter.Options = [.. _filters.Select(u =>u.ToFilterOption())];
        Filter.Operator = _operator == "and" ? FilterOperator.And : FilterOperator.Or;

        if (FilterChanged.HasDelegate)
            await FilterChanged.InvokeAsync(Filter);
    }

    private async Task HandleOnCancel()
    {
        Show = false;
        if (ShowChanged.HasDelegate)
            await ShowChanged.InvokeAsync(Show);
    }

    private void OnFuncChange(int index, StandardFilter func)
    {
        var filter = _filters[index];
        filter.Func = func;
        if (filter.Column.Type == ColumnType.Select)
        {
            if (func is StandardFilter.Contains or StandardFilter.NotContains)
            {
                filter.Type = ExpectedType.Expression;
            }
            else
            {
                filter.Type = ExpectedType.String;
            }
        }
    }

    private void TimeChange(int index, DateTime time)
    {
        var filter = _filters[index];
        filter.Expected = time.ToString();
    }

    private void ValueChange(int index, List<string> values)
    {
        var filter = _filters[index];
        var value = string.Join("\",\"", values.Where(s => s.Length > 0));
        var tt = $"[\"{value}\"]";
        filter.Expected = values.Count == 0 ? "[]" : tt;
    }

    private string GetFuncI18n(StandardFilter filter, ColumnType type)
    {
        return filter switch
        {
            StandardFilter.Contains =>
                 type switch
                 {
                     ColumnType.Text => "Like",
                     ColumnType.Email => "Like",
                     ColumnType.Phone => "Like",
                     ColumnType.Link => "Like",
                     ColumnType.Image => "Like",
                     _ => filter.ToString()
                 },
            StandardFilter.NotContains =>
                 type switch
                 {
                     ColumnType.Text => "Not_Like",
                     ColumnType.Email => "Not_Like",
                     ColumnType.Phone => "Not_Like",
                     ColumnType.Link => "Not_Like",
                     ColumnType.Image => "Not_Like",
                     _ => filter.ToString()
                 },
            _ => filter.ToString()
        };
    }
}